package service

import (
	"context"
	"time"

	"gitee.com/arjunxw/iothub-south-client/config"
	"gorm.io/gorm"
)

type ReportService interface {
	ClearHistoryLog(ctx context.Context, date time.Time) bool
	FindHistory(ctx context.Context, id int64) *ReportHistory
	FindHistoryByReportId(ctx context.Context, reportId string) *ReportHistory
	CreateHistory(ctx context.Context, history *ReportHistory) error
	UpdateHistory(ctx context.Context, history *ReportHistory) error
	DeleteHistory(ctx context.Context, id int64) error
	FindRetryPage(ctx context.Context, page, size int) (total int64, records []*RetryRecord)
	CreateRetry(ctx context.Context, record *RetryRecord) error
	UpdateRetry(ctx context.Context, record *RetryRecord) error
	DeleteRetryByReportId(ctx context.Context, reportId int64) error
}

type reportService struct {
	cfg config.Config
	db  *gorm.DB
}

func NewReportService(cfg config.Config, db *gorm.DB) ReportService {
	service := &reportService{cfg: cfg, db: db}
	return service
}

func (rs *reportService) Init() (err error) {
	if err = rs.db.AutoMigrate(&ReportHistory{}); err != nil {
		return
	}
	if err = rs.db.AutoMigrate(&RetryRecord{}); err != nil {
		return
	}
	rs.init2()
	return
}

func (rs *reportService) init2() {
	go func() {
		var timer *time.Timer
		now := time.Now()
		var nextTimeFunc = func(now time.Time) time.Duration {
			var t time.Time
			if now.Hour() < 1 {
				t = time.Date(now.Year(), now.Month(), now.Day(), 1, 0, 0, 0, now.Location())
			} else if now.Hour() >= 1 {
				t = time.Date(now.Year(), now.Month(), now.Day()+1, 1, 0, 0, 0, now.Location())
			}
			return t.Sub(now)
		}
		for {
			// Calculate the next execution time
			d := nextTimeFunc(now)
			if timer == nil {
				timer = time.NewTimer(d)
			} else {
				timer.Reset(d)
			}
			now = <-timer.C
			t := now.AddDate(0, 0, -2)
			rs.ClearHistoryLog(context.Background(), t)
		}
	}()
}

func (rs *reportService) ClearHistoryLog(ctx context.Context, date time.Time) bool {
	err := rs.getDB(ctx).Delete(&ReportHistory{}, "id > ? AND created_time < ?", 0, date).Error
	if err != nil {
		return false
	}
	return true
}

func (rs *reportService) FindRetryPage(ctx context.Context, page, size int) (total int64, records []*RetryRecord) {
	if page == 0 {
		page = 1
	}
	if size == 0 {
		size = 20
	}

	records = make([]*RetryRecord, 0, size)
	rs.getDB(ctx).Model(&RetryRecord{}).Order("next").Limit(size).Offset((page - 1) * size).Find(&records).Count(&total)
	return
}

func (rs *reportService) CreateRetry(ctx context.Context, record *RetryRecord) error {
	return rs.getDB(ctx).Create(record).Error
}

func (rs *reportService) UpdateRetry(ctx context.Context, record *RetryRecord) error {
	return rs.getDB(ctx).Model(record).Updates(record).Error
}

func (rs *reportService) DeleteRetryByReportId(ctx context.Context, reportId int64) error {
	return rs.getDB(ctx).Delete(&RetryRecord{}, "report_id = ?", reportId).Error
}

func (rs *reportService) getDB(ctx context.Context) *gorm.DB {
	val := ctx.Value(TransactionKey{})
	switch val := val.(type) {
	case *gorm.DB:
		return val
	default:
		return rs.db
	}
}

func (rs *reportService) FindHistory(ctx context.Context, id int64) (history *ReportHistory) {
	history = new(ReportHistory)
	if err := rs.getDB(ctx).First(history, "id = ?", id).Error; err == nil {
		return history
	}
	return nil
}

func (rs *reportService) FindHistoryByReportId(ctx context.Context, reportId string) (history *ReportHistory) {
	history = new(ReportHistory)
	if err := rs.getDB(ctx).First(history, "report_id = ?", reportId).Error; err == nil {
		return history
	}
	return nil
}

func (rs *reportService) CreateHistory(ctx context.Context, history *ReportHistory) error {
	return rs.getDB(ctx).Create(history).Error
}

func (rs *reportService) UpdateHistory(ctx context.Context, history *ReportHistory) error {
	return rs.getDB(ctx).Model(history).Updates(history).Error
}

func (rs *reportService) DeleteHistory(ctx context.Context, id int64) error {
	return rs.getDB(ctx).Delete(&ReportHistory{}, id).Error
}
